home *** CD-ROM | disk | FTP | other *** search
- Attribute VB_Name = "modFriends"
- Option Explicit
- ' Constants for debug functions.
- Global Const DEBUGTOKEN_DebugID = 1
- Global Const DEBUGTOKEN_ClassName = 2
-
- ' The user-defined type used by the
- ' Friend member demo.
- Public Type udtDEMO
- intA As Integer
- lngB As Long
- strC As String
- End Type
-
- ' timeGetTime is used by the Implements
- ' ----------- demo, and by the
- ' object lifetime debug code in this
- ' module.
- Declare Function timeGetTime Lib "winmm.dll" () As Long
-
- ' Storage for the global collection
- ' for debugging object lifetimes.
- ' Used by DebugInit, DebugTerm,
- ' and DebugShow procedures (below).
- Private mcolDebug As New Collection
-
- ' DEBUGGING PROCEDURES
- '
- ' DebugInit
- ' DebugTerm
- ' DebugShow
- '
- ' All objects implement the IDebug
- ' interface, and support it by
- ' calling DebugInit(Me) in their
- ' Initialize events, and DebugTerm Me
- ' in their Terminate events. You can
- ' use DebugShow in the Immediate window
- ' to list an active object, all active
- ' objects, or all objects of a class.
- '
- ' All of the objects in this project are
- ' set up to use these functions.
- '
- ' -------------------------------------
- ' DebugInit is called by each object,
- ' --------- in its Initialize event.
- ' DebugInit adds a debug string for the
- ' object to the global collection, and
- ' returns a unique DebugID for the
- ' object. The method optionally shows
- ' the debug string in the Immediate
- ' window (default is True).
- '
- Public Function DebugInit(ByVal obj As Object, _
- Optional ByVal ShowImmediate As Boolean = True) As Long
- Dim lngDebugID As Long
- Dim strDebug As String
-
- ' Get a unique ID number.
- lngDebugID = GetDebugID
- ' The debug string kept for each
- ' object shows the DebugID, the
- ' class name of the object, and
- ' the time it was initialized
- ' (number of seconds since the
- ' first debug object was created,
- ' expressed as a Double, with the
- ' milliseconds as the fractional
- ' part).
- strDebug = lngDebugID & " " _
- & TypeName(obj) _
- & " (created at " & DebugTime & ")"
- '
- ' Add the string to the collection,
- ' using the unique ID as a key.
- mcolDebug.Add strDebug, CStr(lngDebugID)
- '
- ' The default is to show the debug
- ' string in the Immediate window.
- If ShowImmediate Then Debug.Print strDebug
- '
- ' Return the DebugID. The object must
- ' store this as part of the
- ' implementation of IDebug.
- DebugInit = lngDebugID
- End Function
-
- ' DebugTerm is called by each object,
- ' --------- in its Terminate event.
- ' DebugTerm removes the object's
- ' debug string from the global
- ' collection, and optionally (default
- ' is True) shows the debug string in
- ' the Immediate window.
- '
- Public Sub DebugTerm(ByVal obj As Object, _
- Optional ByVal ShowImmediate As Boolean = True)
-
- Dim idbg As IDebug
-
- On Error Resume Next
- '
- ' Get a reference to the object's
- ' IDebug interface.
- Set idbg = obj
- If Err.Number <> 0 Then
- MsgBox TypeName(obj) & " doesn't implement IDebug; can't record termination.", , "DebugTerm"
- Exit Sub
- End If
- '
- ' The default is to show the debug
- ' string in the Immediate window.
- If ShowImmediate Then Debug.Print _
- mcolDebug(CStr(idbg.DebugID)) _
- & " (Term at " & DebugTime & ")"
- '
- ' Remove the string from the
- ' collection.
- mcolDebug.Remove CStr(idbg.DebugID)
- End Sub
-
- ' DebugShow displays the debug string(s)
- ' --------- for the entire list of
- ' active objects, for all active objects
- ' of a class, or for a particular object.
- ' Call DebugShow from the Immediate
- ' window with no argument (lists all),
- ' a class name (lists all of that class),
- ' an object reference (lists that
- ' object), or the DebugID of an object
- ' (lists that object).
- '
- Public Sub DebugShow(Optional ByVal What As Variant)
- Dim vnt As Variant
- Dim idbg As IDebug
-
- On Error GoTo NoShow
- ' If no argument is supplied, display
- ' all active objects. (It would be
- ' useful to have an optional second
- ' parameter Filename that would let
- ' you dump this to a file; or perhaps
- ' it should dump to the Clipboard.)
- If IsMissing(What) Then
- What = "<All>"
- For Each vnt In mcolDebug
- Debug.Print vnt
- Next
- '
- ' If an object is supplied, use its
- ' DebugID to look up its debug
- ' string.
- ElseIf IsObject(What) Then
- On Error Resume Next
- '
- ' Get a reference to the object's
- ' IDebug interface.
- Set idbg = What
- If Err.Number <> 0 Then
- MsgBox TypeName(What) & " doesn't implement IDebug; can't show debug record.", , "DebugShow"
- Exit Sub
- End If
- '
- Debug.Print mcolDebug(CStr(idbg.DebugID))
- '
- ' If a number is supplied, assume it's
- ' a DebugID and use it to look up
- ' the string.
- ElseIf IsNumeric(What) Then
- Debug.Print mcolDebug(CStr(What))
- '
- ' If it's not a number, assume it's
- ' a string containing the class
- ' name; display all objects with
- ' that class name.
- Else
- For Each vnt In mcolDebug
- If What = GetDebugToken(vnt, DEBUGTOKEN_ClassName) Then
- Debug.Print vnt
- End If
- Next
- End If
- Exit Sub
-
- NoShow:
- If IsObject(What) Then
- MsgBox "Unable to display information. Is this object set up for debugging?", , "DebugShow"
- Else
- MsgBox "Unable to display information for " _
- & What & ". Is this object set up for debugging?", , "DebugShow"
- End If
- End Sub
-
- ' GetDebugString returns an object's
- ' -------------- string from the global
- ' collection.
- '
- Public Function GetDebugString(ByVal obj As Object) As String
- Dim idbg As IDebug
-
- On Error Resume Next
- '
- ' Get a reference to the object's
- ' IDebug interface.
- Set idbg = obj
- GetDebugString = mcolDebug(CStr(idbg.DebugID))
- End Function
-
- ' GetDebugID is used to assign each object
- ' ---------- a unique ID number, for
- ' debugging purposes.
- Public Function GetDebugID() As Long
- Static lngLastID As Long
- lngLastID = lngLastID + 1
- GetDebugID = lngLastID
- End Function
-
- ' GetDebugToken parses the debug string
- ' ------------- for an object and
- ' returns the requested token. Tokens
- ' are separated by single spaces.
- ' (1) DebugID
- ' (2) class name
- '
- ' There are other tokens, but they're
- ' kind of a jumble.
- '
- Public Function GetDebugToken( _
- ByVal DebugString As String, _
- ByVal TokenNumber As Integer) As String
-
- Dim inx1 As Long
- Dim inx2 As Long
- Dim ct As Integer
-
- If TokenNumber <= 0 Then
- Err.Raise vbObjectError + 1060, , _
- "Bad token number in GetDebugToken"
- Else
- inx2 = 1
- For ct = 1 To TokenNumber
- inx1 = inx2
- inx2 = InStr(inx1, DebugString, " ")
- If inx2 = 0 Then Exit For
- Next
- If inx2 = 0 Then
- GetDebugToken = ""
- Else
- GetDebugToken = Mid$(DebugString, inx1 + 1, inx2 - inx1)
- End If
- End If
- End Function
-
- ' DebugTime uses the timeGetTime API to
- ' --------- get milliseconds since
- ' the computer was booted. This is
- ' converted to a Double containing the
- ' number of seconds since the first
- ' debug object was created (s.mmm),
- ' using the first time this function
- ' was called as the base time. (This
- ' makes the time values more useful
- ' than the raw number of milliseconds
- ' since the last boot, which (1) tends
- ' to be a very large number, and (2) can
- ' be negative, as explained below.)
- '
- Public Function DebugTime() As Double
- Static timeBase As Double
- Dim timeCurrent As Double
-
- If timeBase = 0 Then
- ' Initialize the base time. (The
- ' loop allows for the fact that
- ' the time returned by timeGetTime
- ' can pass through zero again, if
- ' the computer is left running
- ' long enough.)
- Do While timeBase = 0
- timeBase = timeGetTime
- Loop
- '
- ' The value returned by timeGetTime
- ' can be negative (see note
- ' below) if the computer has
- ' been running long enough.
- ' Correct for this.
- If timeBase < 0 Then
- timeBase = timeBase + 4294967296#
- End If
- End If
- '
- timeCurrent = timeGetTime
- '
- ' Correct for negative value, if
- ' necessary.
- If timeCurrent < 0 Then
- timeCurrent = timeCurrent + 4294967296#
- End If
- '
- ' Handle the case where timeGetTime
- ' rolls over to zero.
- If timeCurrent < timeBase Then
- DebugTime = (timeCurrent + 4294967296# - timeBase) / 1000#
- Else
- DebugTime = (timeCurrent - timeBase) / 1000#
- End If
- End Function
- ' ----------- timeGetTime -----------
- ' The number of milliseconds since
- ' last boot is an unsigned four-byte
- ' binary integer, which means it can
- ' get bigger than a Long can hold.
- ' When it passes the largest positive
- ' number a Long can hold, 2147483647,
- ' it appears to Basic as if the
- ' number has 'rolled over' and gone
- ' negative. Once it has rolled over,
- ' it continues increasing -- moving
- ' from the largest negative number a
- ' Long can hold up to zero, and then
- ' into positive numbers again.
- '
- ' This creates a 'sawtooth' pattern,
- ' and it works just fine for time
- ' differences (which is what
- ' DebugTime is calculating), except
- ' for that awkward moment when the
- ' rollover happens.
- '
- ' DebugTime solves this problem by
- ' putting the number into a larger
- ' container -- a Double. If the
- ' number is negative, it can be
- ' turned into the number it should
- ' have been by adding 4294967296.
-